package cn.zh.advisor.config;

import cn.zh.advisor.anno.TargetDataSource;
import cn.zh.advisor.component.DataSourceManagement;
import cn.zh.advisor.constant.DataSourceEnum;
import org.aopalliance.aop.Advice;
import org.aopalliance.intercept.MethodInterceptor;
import org.aopalliance.intercept.MethodInvocation;
import org.springframework.aop.Pointcut;
import org.springframework.aop.PointcutAdvisor;
import org.springframework.aop.support.annotation.AnnotationMatchingPointcut;
import org.springframework.beans.factory.config.BeanDefinition;
import org.springframework.context.annotation.Role;
import org.springframework.core.BridgeMethodResolver;
import org.springframework.core.annotation.AnnotatedElementUtils;
import org.springframework.core.annotation.AnnotationAttributes;
import org.springframework.stereotype.Component;
import org.springframework.util.ClassUtils;

import java.lang.reflect.AnnotatedElement;
import java.lang.reflect.Method;


/**
 * 仿@DS注解Advisor精简实现
 * Advisor会被InfrastructureAdvisorAutoProxyCreator会将所有Advisor扫描，再将被这个注解标明Advisor添加到候选Advisor中
 * 即加入到bean初始化后去匹配的Advisor列表中
 */
@Component
@Role(BeanDefinition.ROLE_INFRASTRUCTURE)
public class MyAdvisor implements PointcutAdvisor {

    @Override
    public Pointcut getPointcut() {
        return new AnnotationMatchingPointcut(TargetDataSource.class, true);
    }

    @Override
    public Advice getAdvice() {
        return new MethodInterceptor() {
            @Override
            public Object invoke(MethodInvocation invocation) throws Throwable {
                String key = computeDsKey(invocation);
                DataSourceManagement.flag.set(key);
                return invocation.proceed();
            }
        };
    }

    @Override
    public boolean isPerInstance() {
        return false;
    }

    /**
     * 计算数据源key
     *
     * @param invocation 方法调用器
     * @return
     */
    private String computeDsKey(MethodInvocation invocation) {
        String key;
        Method method = invocation.getMethod();
        // 1.从当前方法找
        key = findDataSourceAttribute(method);
        if (key != null) {
            return key;
        }
        Class<?> targetObject = invocation.getThis().getClass();
        // 若是CGLIB代理则获取其父类
        Class<?> userClass = ClassUtils.getUserClass(targetObject);
        // 获取实现类的方法声明.  method: 接口的方法, specificMethod: 实现类方法
        // 例：若该方法是BaseMapper的selectById，则method对应的是BaseMapper接口上的selectById();specificMethod是代理类的selectById()
        Method specificMethod = ClassUtils.getMostSpecificMethod(method, userClass);
        // 获取实现类的原始方法(重写前的方法)，若找不到则返回桥接方法或传入方法
        // 内部的cache会存储桥接方法对应的原始方法，不是桥接方法则直接返回，是桥接方法则去找原始方法
        specificMethod = BridgeMethodResolver.findBridgedMethod(specificMethod);

        // 2.从实现类方法(或原始方法)获取
        key = findDataSourceAttribute(specificMethod);
        if (key != null) {
            return key;
        }

        // 3.从当前方法声明的类查找
        key = findDataSourceAttribute(userClass);
        if (key != null) {
            return key;
        }

        // 4.从所有的接口中查找
        for (Class<?> interfaceClazz : ClassUtils.getAllInterfacesForClassAsSet(userClass)) {
            key = findDataSourceAttribute(interfaceClazz);
            // 只取第一个
            if (key != null) {
                return key;
            }
        }

        // 5.从方法的声明类中查找
        key = findDataSourceAttribute(method.getDeclaringClass());
        if (key != null && ClassUtils.isUserLevelMethod(method)) {
            return key;
        }

        // 6.设置默认值
        return DataSourceEnum.MASTER.name();

    }

    /**
     * 获取元素上TargetDataSource注解的value值
     * @param ae 可被注解元素
     * @return
     */
    private String findDataSourceAttribute(AnnotatedElement ae) {
        AnnotationAttributes attributes = AnnotatedElementUtils.getMergedAnnotationAttributes(ae, TargetDataSource.class);
        if (attributes != null) {
            return ((DataSourceEnum) attributes.get("value")).name();
        }
        return null;
    }
}
